package com.azusa.common.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.azusa.common.exception.ErrorCode;
import com.azusa.common.exception.ErrorInfo;
import com.azusa.common.exception.ErrorInfoException;
import com.azusa.common.http.callback.IHttpCallBack;
import com.azusa.common.http.callback.IStreamReaderCallBack;
import com.azusa.common.http.callback.IStreamWriterCallBack;
import com.azusa.common.utils.Assert;
import com.azusa.common.utils.IOUtil;

/**
 * 
 * <p>类功能说明:http具体基本实现类。只提供最基本的get和post方式的http访问。该类出了实现基本的http访问外，还用于继承。</p>
 * <p>Class Function Description:</p>
 * <p>Title: HttpClient.java</p> 
 * <p>Description:Azusa</p>
 * <p>Copyright: Copyright © 2013 Leonard Lan.All Rights Reserved.</p>
 * <p>Company:Azusa</p>
 * @author Leonard Lan
   @date Aug 27, 2013 10:43:51 AM
 * @version v1.0
 * @since azusa-commons v1.0.0
 */
public class HttpClient {
	/**
	 * 使用get的方式访问目标url
	 * @param httpRequest
	 * @return
	 * @throws ErrorInfoException
	 */
	public String get(final HttpRequest httpRequest) throws ErrorInfoException{
		final List<String> listStr = new ArrayList<String>();
		String result = null;
		
		forGet(httpRequest,new IStreamReaderCallBack(){
			public void read(InputStream is) throws IOException {
				listStr.add(IOUtil.getStreamAsString(is, httpRequest.getCharset()));
			}			
		});		
		
		if(listStr.size() > 0){
			result = listStr.get(0);
		}
		return result;
	}
	
	/**
	 * 使用post的方式访问目标url
	 * @param httpRequest
	 * @return
	 * @throws ErrorInfoException
	 */
	public String post(final HttpRequest httpRequest) throws ErrorInfoException{
		final List<String> listStr = new ArrayList<String>();
		String result = null;
		
		forPost(httpRequest,new IStreamReaderCallBack(){
			public void read(InputStream is) throws IOException {
				listStr.add(IOUtil.getStreamAsString(is, httpRequest.getCharset()));
			}			
		});		
		
		if(listStr.size() > 0){
			result = listStr.get(0);
		}
		return result;
	}
	
	/**
	 * Get访问方式的具体实现
	 * @param httpRequest
	 * @param httpCallBack
	 * @throws ErrorInfoException
	 */
	protected void forGet(HttpRequest httpRequest,IHttpCallBack httpCallBack) throws ErrorInfoException{
		HttpURLConnection conn = null;
		if(!(httpCallBack instanceof IStreamReaderCallBack)){
			throw new ErrorInfoException(new ErrorInfo("HTTP_ERROR_01",ErrorCode.getInstance().getErrorDescribeByCode("HTTP_ERROR_01")));
		}
		try{
			String urlStr = buildGetURL(httpRequest.getUrl(),httpRequest.getParameters(),httpRequest.getCharset());
			conn = getConnection(urlStr,"GET",httpRequest);
			
			InputStream errorStream = conn.getErrorStream();
			if(errorStream == null){
				((IStreamReaderCallBack)httpCallBack).read(conn.getInputStream());
			}else{
				String msg = IOUtil.getStreamAsString(errorStream,httpRequest.getCharset());
				if (Assert.isNullOrEmpty(msg)) {
					throw new IOException("[http状态码:" + conn.getResponseCode() + "]:" + conn.getResponseMessage());
				} else {
					throw new IOException("[http状态码:" + conn.getResponseCode() + "]:" +msg);
				}
			}
		}catch(IOException e){
			throw new ErrorInfoException(new ErrorInfo("HTTP_ERROR_02",ErrorCode.getInstance().getErrorDescribeByCode("HTTP_ERROR_02")),e);
		}finally{
			if (conn != null) {
				conn.disconnect();
			}
		}
	} 
	
	/**
	 * Post访问方式的具体实现
	 * @param httpRequest
	 * @param httpCallBack
	 * @throws ErrorInfoException
	 */
	protected void forPost(HttpRequest httpRequest,IHttpCallBack httpCallBack) throws ErrorInfoException{
		HttpURLConnection conn = null;
		OutputStream out = null;
		try{
		    String postParameStr = buildParameterURL(httpRequest.getParameters(),httpRequest.getCharset());
		    byte[] content = {};
			if (postParameStr != null) {
				content = postParameStr.getBytes(httpRequest.getCharset());
			}
			
			conn = getConnection(httpRequest.getUrl(),"POST",httpRequest);
			out = conn.getOutputStream();
			if (content != null && content.length > 0) {
				out.write(content);
			}			
			if (httpCallBack instanceof IStreamWriterCallBack) {
				((IStreamWriterCallBack)httpCallBack).write(out);
			}
			
			InputStream errorStream = conn.getErrorStream();
			if (errorStream == null) {
				if (httpCallBack instanceof IStreamReaderCallBack) {
					((IStreamReaderCallBack)httpCallBack).read(conn.getInputStream());
				}
			} else {
				String msg = IOUtil.getStreamAsString(errorStream,httpRequest.getCharset());
				if (Assert.isNullOrEmpty(msg)) {
					throw new IOException("[http状态码:" + conn.getResponseCode() + "]:" + conn.getResponseMessage());
				} else {
					throw new IOException("[http状态码:" + conn.getResponseCode() + "]:" +msg);
				}
			}
		}catch(IOException e){
			throw new ErrorInfoException(new ErrorInfo("HTTP_ERROR_02",ErrorCode.getInstance().getErrorDescribeByCode("HTTP_ERROR_02")),e);
		}finally{
			if (out != null) {
				try{
				    out.close();
				}catch(IOException e){
					throw new ErrorInfoException(new ErrorInfo("HTTP_ERROR_03",ErrorCode.getInstance().getErrorDescribeByCode("HTTP_ERROR_03")),e);
				}
			}
			if (conn != null) {
				conn.disconnect();
			}
		}
	}
	
	/**
	 * 获取HttpConnection。使用HttpRequest中的配置，获取HttpConnection。
	 * @param urlStr
	 * @param method
	 * @param request
	 * @return
	 * @throws IOException
	 */
	protected HttpURLConnection getConnection(String urlStr, String method, HttpRequest request) throws IOException{
		URL url = new URL(urlStr);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		
		conn.setRequestMethod(method);
		conn.setConnectTimeout(request.getConnectionTimeout());
		conn.setReadTimeout(request.getReadTimeout());
		conn.setDoInput(true);
		conn.setDoOutput(true);
		
		if(!Assert.isNullOrEmpty(request.getHeaders())){
			for(String key : request.getHeaders().keySet()){
				conn.addRequestProperty(key, request.getHeaders().get(key));
			}
		}
		
		return conn;
	}
	
	/**
	 * 将参数拼接成字符串。post访问方式可以直接使用该字符串，get访问方式需要使用拼接后的字符串与url再次拼接后才能使用。
	 * @param params
	 * @param charset
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	protected static String buildParameterURL(Map<String, String> params, String charset) throws UnsupportedEncodingException{
		if (params == null || params.size() < 1) {
			return null;
		}

		StringBuilder query = new StringBuilder();
		boolean hasParam = false;

		for (Entry<String, String> entry : params.entrySet()) {
			String name = entry.getKey();
			String value = entry.getValue();
			if (name != null && name != "" 
				&& value != null && value != "") {
				if (hasParam) {
					query.append("&");
				} else {
					hasParam = true;
				}

				query.append(name).append("=").append(URLEncoder.encode(value, charset));
			}
		}

		return query.toString();
	}
	
	/**
	 * 根据目标访问地址和参数拼接适用于get的url
	 * @param url
	 * @param params
	 * @param charset
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	protected String buildGetURL(String url,Map<String, String> params, String charset) throws UnsupportedEncodingException {
		String query = buildParameterURL(params, charset);
		
		if (query != null && query != "") {
			if (url.indexOf("?") != -1) {
				url = url + "&" + query;
			} else {
				url = url + "?" + query;
			}
		}
		return url;
	}
	
	
}
